package com.tsd.core.aspect;

import com.tsd.core.utils.LockUtil;
import com.tsd.core.vo.MethodDuration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * @ClassName: MethodDurationAspect
 * @Description: 监控方法执行时间的切面
 * @Author: Hillpool
 * @Date: 2023/9/9 14:15
 * @Version: 1.0
 */
@Aspect
@Component
public class MethodDurationAspect {

    public static final Logger LOGGER = LoggerFactory.getLogger(MethodDurationAspect.class);
    static Map<String, MethodDuration> durationMap = new ConcurrentHashMap<>();
    final private int LOCK_TIME_MS = 100;
    /**
     * 环绕通知, 围绕着方法执行
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     * @Around 可以用来在调用一个具体方法前和调用后来完成一些具体的任务。
     * <p>
     * 方式一：单用 @Around("execution(* com.tsd..controller.*.*(..))")可以
     * 方式二：用@Pointcut和@Around联合注解也可以
     * <p>
     * <p>
     */
    @Around("execution(* com.tsd..controller.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        try {
            return joinPoint.proceed();
        } finally {
            long duration = System.currentTimeMillis() - start;
            String key = className + "." + methodName;
            Lock lock = LockUtil.getLock(key);
            if (lock.tryLock(LOCK_TIME_MS, TimeUnit.MILLISECONDS)) {
                try {
                    MethodDuration t = durationMap.get(key);
                    if (t == null) {
                        t = new MethodDuration();
                        t.setClass_method(key);
                        t.setCount(1);
                        t.setMax(duration);
                        t.setMin(duration);
                        t.setSum(duration);
                        durationMap.put(key, t);
                    } else {
                        t.setCount(t.getCount() + 1);
                        if (duration > t.getMax()) {
                            t.setMax(duration);
                        }
                        if (duration < t.getMin()) {
                            t.setMin(duration);
                        }
                        t.setSum(t.getSum() + duration);
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 返回执行时长Map
     *
     * @return
     */
    public static Map<String, MethodDuration> getDurationMap() {
        return durationMap;
    }
}
